package com.info.zhiduoduo.pay.sdk.kltongpaysdk.util;

import com.alibaba.fastjson.JSONObject;

import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class RSAUtils {

	/**
	 * 算法
	 */
	public static final String ALGORITHM = "RSA";

	/**
	 * 签名算法
	 */
	public static final String SIGN_ALGORITHM = "SHA256WithRSA";

	/**
	 * 公钥Map Key
	 */
	public static final String PUBLIC_KEY = "publicKey";

	/**
	 * 私钥Map Key
	 */
	public static final String PRIVATE_KEY = "privateKey";

	public static final String UTF_8 = "UTF-8";

	public static String sortAndRsa256Sign(String orginStr, String privateKey) throws Exception {
		JSONObject jsonObject = JSONObject.parseObject(orginStr);
		JSONObject headJson = jsonObject.getJSONObject("head");
		JSONObject contentJson = jsonObject.getJSONObject("content");

		//放入TreeMap后,按照Key值自然排序！
		Map signMap = new TreeMap();
		//报文头
		for (Map.Entry entry : headJson.entrySet()) {
			signMap.put(entry.getKey(), String.valueOf(entry.getValue()));
		}
		//报文体
		for (Map.Entry entry : contentJson.entrySet()) {
			signMap.put(entry.getKey(), String.valueOf(entry.getValue()));
		}

		StringBuilder stringBuilder = new StringBuilder();
		Set<Map.Entry<String, String>> entrySet = signMap.entrySet();
		for (Map.Entry entry : entrySet) {
			if (entry.getValue() != null && !((String) entry.getValue()).trim().equals(""))
				stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
		}

		//删除最后一个&号
		String signOrgin = stringBuilder.substring(0, stringBuilder.length() - 1);
		String sign = rsa256Sign(signOrgin, privateKey, UTF_8);
		//添加签名
		jsonObject.getJSONObject("head").put("sign", sign);
		return jsonObject.toJSONString();
	}


	/**
	 * 加签
	 *
	 * @param content    签名原串
	 * @param privateKey 私钥
	 * @param charset    字符集
	 * @return
	 * @throws Exception
	 */
	public static String rsa256Sign(String content, String privateKey, String charset) throws Exception {
		byte[] keyBytes = Base64.getDecoder().decode(privateKey);
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
		PrivateKey priKey = keyFactory.generatePrivate(keySpec);
		Signature signature = Signature.getInstance(SIGN_ALGORITHM);
		signature.initSign(priKey);
		if (charset != null && !charset.trim().equals("")) {
			signature.update(content.getBytes(charset));
		} else {
			signature.update(content.getBytes());
		}
		byte[] signed = signature.sign();
		return Base64.getEncoder().encodeToString(signed);
	}

	/**
	 * 验证开联通返回的数据,加密是否安全
	 *
	 * @param orginJson 原始json,其中包含了开联通传过来的sign
	 * @return
	 */
	public static boolean checkSign(String orginJson, String publicKey) {
		JSONObject jsonObject = JSONObject.parseObject(orginJson);
		String sign = (String) jsonObject.remove("sign");

		//放入TreeMap后,按照Key值自然排序！
		Map signMap = new TreeMap();
		for (Map.Entry entry : jsonObject.entrySet()) {
			signMap.put(entry.getKey(), String.valueOf(entry.getValue()));
		}

		StringBuilder stringBuilder = new StringBuilder();
		Set<Map.Entry<String, String>> entrySet = signMap.entrySet();
		for (Map.Entry entry : entrySet) {
			if (entry.getValue() != null && !((String) entry.getValue()).trim().equals(""))
				stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
		}

		//删除最后一个&号
		String content = stringBuilder.substring(0, stringBuilder.length() - 1);
		try {
			return rsa256CheckSign(content, sign, publicKey, UTF_8);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return false;
	}

	/**
	 * 验签
	 *
	 * @param content   验签原串
	 * @param sign      签名
	 * @param publicKey 公钥
	 * @param charset   字符集
	 * @return
	 * @throws Exception
	 */
	public static boolean rsa256CheckSign(String content, String sign, String publicKey, String charset) throws Exception {
		byte[] keyBytes = Base64.getDecoder().decode(publicKey);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
		PublicKey pubKey = keyFactory.generatePublic(keySpec);
		Signature signature = Signature.getInstance(SIGN_ALGORITHM);
		signature.initVerify(pubKey);
		if (charset != null && !charset.trim().equals("")) {
			signature.update(content.getBytes(charset));
		} else {
			signature.update(content.getBytes());
		}
		return signature.verify(Base64.getDecoder().decode(sign.getBytes()));
	}

	/**
	 * 通过PFX证书获取RSA公钥和私钥
	 *
	 * @param pfxPath  证书存放路径
	 * @param password 证书密码
	 * @return
	 */
	public static Map<String, String> loadKeyByFile(String pfxPath, String password) throws Exception {
		KeyStore ks = KeyStore.getInstance("PKCS12");
		FileInputStream fis = new FileInputStream(pfxPath);
		char[] nPassword;
		if (password == null || password.trim().equals("")) {
			nPassword = null;
		} else {
			nPassword = password.toCharArray();
		}
		ks.load(fis, nPassword);
		fis.close();
		Enumeration enumas = ks.aliases();
		String keyAlias = null;
		if (enumas.hasMoreElements()) {
			keyAlias = (String) enumas.nextElement();
		}
		PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);
		Certificate cert = ks.getCertificate(keyAlias);
		PublicKey pubkey = cert.getPublicKey();
		String publicKey = Base64.getEncoder().encodeToString(pubkey.getEncoded());
		String privateKey = Base64.getEncoder().encodeToString(prikey.getEncoded());
		Map<String, String> keyMap = new HashMap<>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}

	public static void main(String[] args) throws Exception {
		Map<String, String> map = RSAUtils.loadKeyByFile("F:\\项目文件\\智多多\\开联通支付\\903310112340001-1-kltong(1).pfx", "123456");

		String privateKey = map.get(PRIVATE_KEY);
		String publicKey = map.get(PUBLIC_KEY);

		System.out.println("私钥=" + privateKey);
		System.out.println("公钥=" + publicKey);

		String content = "orderNo=45275427654927&originalRequestId=148085116478996480&productDesc=商品描述中文测试";

		String sign = RSAUtils.rsa256Sign(content, privateKey, "UTF-8");
		System.out.println("签名=" + sign);

		System.out.println(RSAUtils.rsa256CheckSign(content, sign, publicKey, UTF_8));
	}
}
